#include "SpaceFortress.h"

#include "BuildingManager.h"

BlueprintManager BuildingManager::bp_manager;

BuildingManager::BuildingManager()
{
	max_buildings = 300;
	curr_buildings = 0;
	construction_limit = 10;
}

BuildingManager::~BuildingManager()
{

}

void BuildingManager::Initialize(OpenVector<uint32>& comodities)
{
	comodities = bp_manager.Initialize();
}

void BuildingManager::PlaceMarketOrders(BidData* buy_data, BidData* sell_data)
{
	input_manager.PlaceMarketBuys(buy_data);
	output_manager.PlaceMarketSells(sell_data);
}

void BuildingManager::RunProduction()
{
	// need to get the minimum and maximum ratios.
	input_manager.CalculateInputRatio(); // how does this affect consumers? Consumers shouldn't set a minimum ratio, they should consume everything they have.
	output_manager.CalculateOutputRatio();

	Entity* entities = NULL;
	float* ratios = NULL;
	const uint32 output_count = output_manager.GetEntityCount();
	output_manager.InitializeProduction(entities, ratios);
	input_manager.FindMinProduction(output_count, entities, ratios);

	output_manager.OverrideRatios(output_count, entities, ratios);
	input_manager.OverrideRatio(output_count, entities, ratios);

	input_manager.ConsumeInput();
	output_manager.ProduceOutput();

	// Production is finished. Delete the buffers.
	delete[] entities;
	delete[] ratios;
}

/*
	Production and consumption use separate BidData arrays
	*/
void BuildingManager::ProcessMarketResults(const BidData* const buy_data, const BidData* const consumption_data, const BidData* const sell_data,
	const float* const mean, const float* const supply, const float* demand, const uint32 num_resources)
{
	input_manager.RegisterBuyResults(buy_data, mean, supply, demand, num_resources);

	output_manager.RegisterSellResults(sell_data, mean, supply, demand, num_resources);

	finance_manager.RegisterBuyResults(buy_data, num_resources);
	finance_manager.RegisterSellResults(sell_data, num_resources);

	// consumption.
}

void BuildingManager::ProcessDefunctEntities(OpenMap<uint32, uint32>& removed_buyers, OpenMap<uint32, uint32>& removed_sellers)
{
	// Post market turn processing.
	// Take care of the entities.
	// remove bankrupt entities.
	OpenVector<Entity> defunct_entities;
	OpenVector<uint32> defunct_buyers;
	OpenVector<uint32> defunct_sellers;
	finance_manager.FindBankruptEntities(defunct_entities);
	output_manager.RemoveOutputData(defunct_entities, defunct_sellers);
	input_manager.RemoveInputData(defunct_entities, defunct_buyers);
	// remove consumption.

	for (uint32 i = 0; i < defunct_entities.size; i++) {
		entity_manager.Destroy(defunct_entities.data[i]);
	}

	// Transfer all the data into maps
	for (uint32 i = 0; i < defunct_buyers.size; i++) {
		// check to see if comodity is already in the map.
		const uint32 idx = removed_buyers.Index(defunct_buyers.data[i]);
		if (removed_buyers.key[idx] == defunct_buyers.data[i]) {
			removed_buyers.data[idx]++;
		} else {
			removed_buyers.Insert(defunct_buyers.data[i], 1);
		}
	}

	for (uint32 i = 0; i < defunct_sellers.size; i++) {
		// check to see if comodity is already in the map.
		const uint32 idx = removed_buyers.Index(defunct_sellers.data[i]);
		if (removed_sellers.key[idx] == defunct_sellers.data[i]) {
			removed_sellers.data[idx]++;
		}
		else {
			removed_sellers.Insert(defunct_sellers.data[i], 1);
		}
	}
}

// place a limit on the number of buildings that can be made in a single market turn.
// later on in development this could be changed from a hard cap to a soft cap with population, infrastructure, and banking(?).
// when a colony is first constructed it may require special treatment.
// it could go through multiple rounds of allocating buildings before continueing.
// new colonies have a low limit but every building is allocated at the start.
//
// for the moment we don't look at any associated costs, this should change later on.
void BuildingManager::AllocateNewProduction(const float* const comodity_mean, const uint32 output_hash,
	OpenMap<uint32, uint32>& new_buyers, OpenMap<uint32, uint32>& new_sellers)
{
	// add new entities to take the place of the old ones.
	const uint32 available_slots = GetAvailableSlots();
	Entity* new_entities = new Entity[available_slots];
	for (uint32 i = 0; i < available_slots; i++) {
		new_entities[i] = entity_manager.Create();
	}

	finance_manager.AllocateFinance(new_entities, available_slots);

	// Find the blueprint based on the comodity hash.
	// Do not delete these pointers, they point directly into the blueprint data.
	uint32 input_start, input_count;
	float* input_rates;
	uint32* input_indexes;
	bp_manager.GetBlueprintInput(output_hash, input_start, input_count, input_rates, input_indexes);

	const uint32 cost = input_manager.AllocateStorage(new_entities, available_slots, input_indexes, input_rates, input_count, comodity_mean);

	// Do not delete these pointers, they point directly into the blueprint data.
	uint32 output_start, output_count;
	float* output_rates;
	uint32* output_indexes;
	bp_manager.GetBlueprintOutput(output_hash, output_start, output_count, output_rates, output_indexes);

	output_manager.AllocateStorage(new_entities, available_slots, cost, output_indexes, output_rates, output_count, comodity_mean);


	for (uint32 i = 0; i < input_count; i++) {
		new_buyers.Insert(input_indexes[i], available_slots);
	}
	for (uint32 i = 0; i < output_count; i++) {
		new_sellers.Insert(output_indexes[i], available_slots);
	}

	delete[] new_entities;
}